package com.gitee.wsl.unit.angle.ext

import com.gitee.wsl.unit.angle.AngleUnit
import com.gitee.wsl.unit.angle.toDegrees

fun AngleUnit.normalize180() = if (degrees in -180.0..180.0) this else AngleUnit.normalizeAngle180(degrees).toDegrees()

fun AngleUnit.normalize360() = if (degrees in 0.0..360.0) this else AngleUnit.normalizeAngle360(degrees).toDegrees()

fun AngleUnit.normalizeLatitude() = if (degrees in -90.0..90.0) this else AngleUnit.normalizeLatitude(degrees).toDegrees()

fun AngleUnit.normalizeLongitude() = if (degrees in -180.0..180.0) this else AngleUnit.normalizeLongitude(degrees).toDegrees()

/**
 * Restricts an angle to the range [-180, +180] degrees, wrapping angles outside the range. Wrapping takes place as
 * though traversing the edge of a unit circle; angles less than -180 wrap back to +180, while angles greater than
 * +180 wrap back to -180.
 *
 * @param degrees the angle to wrap in degrees
 *
 * @return the specified angle wrapped to [-180, +180] degrees
 */
fun AngleUnit.Companion.normalizeAngle180(degrees: Double): Double {
    val angle = degrees % 360
    return if (angle > 180) angle - 360 else if (angle < -180) 360 + angle else angle
}

/**
 * Restricts an angle to the range [0, 360] degrees, wrapping angles outside the range. Wrapping takes place as
 * though traversing the edge of a unit circle; angles less than 0 wrap back to 360, while angles greater than 360
 * wrap back to 0.
 *
 * @param degrees the angle to wrap in degrees
 *
 * @return the specified angle wrapped to [0, 360] degrees
 */
fun AngleUnit.Companion.normalizeAngle360(degrees: Double): Double {
    val angle = degrees % 360
    return if (angle >= 0) angle else 360 + angle
}

/**
 * Restricts an angle to the range [-90, +90] degrees, wrapping angles outside the range. Wrapping takes place along
 * a line of constant longitude which may pass through the poles. In which case, 135 degrees normalizes to 45
 * degrees; 181 degrees normalizes to -1 degree.
 *
 * @param degrees the angle to wrap in degrees
 *
 * @return the specified angle wrapped to the range [-90, +90] degrees
 */
fun AngleUnit.Companion.normalizeLatitude(degrees: Double): Double {
    val lat = degrees % 180
    val normalizedLat = if (lat > 90) 180 - lat else if (lat < -90) -180 - lat else lat
    // Determine whether the latitude is in the north or south hemisphere
    val numEquatorCrosses = (degrees / 180).toInt()
    return if (numEquatorCrosses % 2 == 0) normalizedLat else -normalizedLat
}

/**
 * Restricts an angle to the range [-180, +180] degrees, wrapping angles outside the range. Wrapping takes place as
 * though traversing a line of constant latitude which may pass through the antimeridian; angles less than -180 wrap
 * back to +180, while angles greater than +180 wrap back to -180.
 *
 * @param degrees the angle to wrap in degrees
 *
 * @return the specified angle wrapped to the range [-180, +180] degrees
 */
fun AngleUnit.Companion.normalizeLongitude(degrees: Double): Double {
    val lon = degrees % 360
    return if (lon > 180) lon - 360 else if (lon < -180) 360 + lon else lon
}
